import { PureComponent } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import escape from 'lodash.escape';
import CodeCell from './CodeCellWithGram';
import { computeOldLineNumber, computeNewLineNumber } from './utils';
import { createEventsBindingSelector } from './selectors';
import { changePropType, eventsPropType, classNamesPropType } from './propTypes';
import './Change.css';
import utils from 'utils';

const SIDE_OLD = 0;
const SIDE_NEW = 1;
const NO_EDITS = [[], []];

const renderCells = (args) => {
  const {
    change,
    side,
    edits,
    selected,
    customClassNames,
    customEvents,
    onRenderCode,
    bindGutterEvents,
    bindCodeEvents,
    anchorID,
    gutterAnchor,
    gutterAnchorTarget,
    genWatermarkimage,
    // 每行内容的id
    changeID,
  } = args;

  if (!change) {
    return [
      <td
        key="gutter"
        className={classNames('diff-gutter', 'diff-gutter-omit', customClassNames.gutter)}
      />,
      <td
        key="code"
        className={classNames(
          'diff-code',
          'diff-code-omit',
          { 'diff-code-omit-image': utils.isGenWatermarkimage(genWatermarkimage) },
          customClassNames.code,
        )}
      />,
    ];
  }

  const { type, content: originalContent } = change;
  const line = side === SIDE_OLD ? computeOldLineNumber(change) : computeNewLineNumber(change);

  let content;
  const isExpanded = originalContent.indexOf('$BLOB_EXPANDED$') !== -1;
  // 以 $BLOB_EXPANDED$ 结尾的行为展开行的标识
  if (isExpanded) {
    // 如果是展开的行，去掉结尾的标识
    content = originalContent.replace('$BLOB_EXPANDED$', '');
  } else {
    content = originalContent;
  }

  const boundGutterEvents = bindGutterEvents(customEvents.gutter, change);

  const gutterClassName = classNames(
    'diff-gutter',
    `diff-gutter-${type}`,
    customClassNames.gutter,
    { 'diff-gutter-selected': selected },
    { 'diff-code-image': `${type}` === 'insert' && utils.isGenWatermarkimage(genWatermarkimage) },
    {
      'diff-code-delet-image':
        `${type}` === 'delete' && utils.isGenWatermarkimage(genWatermarkimage),
    },
    {
      'diff-code-gutter-normal-image':
        `${type}` === 'normal' && utils.isGenWatermarkimage(genWatermarkimage),
    },
  );
  const gutterProps = {
    key: 'gutter',
    id: anchorID,
    className: gutterClassName,
    'data-line-number': line,
    children: gutterAnchor ? <a href={`#${gutterAnchorTarget}`} data-line-number={line} /> : null,
    ...boundGutterEvents,
  };
  const boundCodeEvents = bindCodeEvents(customEvents.code, change);
  const codeClassName = classNames(
    'diff-code',
    `diff-code-${type}`,
    customClassNames.code,
    { 'diff-code-selected': selected },
    { 'diff-code-image': `${type}` === 'insert' && utils.isGenWatermarkimage(genWatermarkimage) },
    {
      'diff-code-delet-image':
        `${type}` === 'delete' && utils.isGenWatermarkimage(genWatermarkimage),
    },
  );

  const codeProps = {
    key: 'code',
    className: codeClassName,
    onRender: onRenderCode,
    'data-line-number': `${change.oldLineNumber}-${change.newLineNumber}`,
    // 标记是否删除行
    isDelete: type === 'delete' ? '1' : '0',
    side: side === SIDE_OLD ? 'left' : 'right',
    id: changeID,
    // genWatermarkimage,
    ...boundCodeEvents,
  };

  if (!edits.length) {
    return [<td {...gutterProps} />, <CodeCell {...codeProps} text={content} />];
  }

  const { html: editMarkedHTML, lastIndex } = edits.reduce(
    (result, [start, length]) => {
      const normalText = content.slice(result.lastIndex, start);
      const editText = content.substr(start, length);

      result.html += `${escape(normalText)}<mark class="diff-code-edit">${escape(editText)}</mark>`;
      result.lastIndex = start + length;

      return result;
    },
    { html: '', lastIndex: 0 },
  );
  const tailHTML = escape(content.substring(lastIndex));

  return [<td {...gutterProps} />, <CodeCell {...codeProps} html={editMarkedHTML + tailHTML} />];
};

export default class SplitChange extends PureComponent {
  bindOldGutterEvents = createEventsBindingSelector();

  bindNewGutterEvents = createEventsBindingSelector();

  bindOldCodeEvents = createEventsBindingSelector();

  bindNewCodeEvents = createEventsBindingSelector();

  static propTypes = {
    monotonous: PropTypes.bool.isRequired,
    oldChange: changePropType,
    newChange: changePropType,
    oldSelected: PropTypes.bool.isRequired,
    newSelected: PropTypes.bool.isRequired,
    markEdits: PropTypes.func,
    customEvents: eventsPropType,
    customClassNames: classNamesPropType,
    onRenderCode: PropTypes.func,
  };

  static defaultProps = {
    customEvents: {},
    markEdits() {
      return NO_EDITS;
    },
    onRenderCode() {},
  };

  render() {
    const {
      oldChange,
      newChange,
      oldSelected,
      newSelected,
      monotonous,
      markEdits,
      customClassNames,
      customEvents,
      onRenderCode,
      generateAnchorID,
      gutterAnchor,
      generateChangeID,
      left_lsif_version,
      right_lsif_version,
      positionMap,
      diff,
      genWatermarkimage,
    } = this.props;

    const [oldEdits, newEdits] = markEdits(oldChange, newChange);

    const commons = {
      monotonous,
      customClassNames,
      customEvents,
      onRenderCode,
      left_lsif_version,
      right_lsif_version,
      positionMap,
      diff,
    };
    const oldAnchorID = oldChange && generateAnchorID(oldChange);
    const oldChangeID = generateChangeID && oldChange && generateChangeID(oldChange);
    const oldArgs = {
      ...commons,
      change: oldChange,
      side: SIDE_OLD,
      edits: oldEdits,
      selected: oldSelected,
      bindGutterEvents: this.bindOldGutterEvents,
      bindCodeEvents: this.bindOldCodeEvents,
      anchorID: oldAnchorID,
      changeID: oldChangeID,
      gutterAnchor,
      gutterAnchorTarget: oldAnchorID,
      genWatermarkimage,
    };
    const newAnchorID = newChange && generateAnchorID(newChange);
    const newChangeID = generateChangeID && newChange && generateChangeID(newChange);
    const newArgs = {
      ...commons,
      change: newChange,
      side: SIDE_NEW,
      edits: newEdits,
      selected: newSelected,
      bindGutterEvents: this.bindNewGutterEvents,
      bindCodeEvents: this.bindNewCodeEvents,
      anchorID: oldChange === newChange ? undefined : newAnchorID,
      changeID: oldChange === newChange ? undefined : newChangeID,
      gutterAnchor,
      gutterAnchorTarget: oldChange === newChange ? oldAnchorID : newAnchorID,
      genWatermarkimage,
    };

    if (monotonous) {
      return (
        <tr className={classNames('diff-line', customClassNames.line)}>
          {renderCells(oldChange ? oldArgs : newArgs)}
        </tr>
      );
    }

    const lineTypeClassName = ((oldChange, newChange) => {
      if (oldChange && !newChange) {
        return 'diff-line-old-only';
      }

      if (!oldChange && newChange) {
        return 'diff-line-new-only';
      }

      if (oldChange === newChange) {
        // 以 $BLOB_EXPANDED$ 结尾的行为展开行的标识，使用 diff-line-expanded 颜色
        if (oldChange.content.indexOf('$BLOB_EXPANDED$') === -1) {
          return 'diff-line-normal';
        } else {
          return utils.isGenWatermarkimage(genWatermarkimage)
            ? 'diff-line-expanded diff-line-expanded-wartimage'
            : 'diff-line-expanded';
        }
      }

      return 'diff-line-compare';
    })(oldChange, newChange);

    return (
      <tr className={classNames('diff-line', customClassNames.line, lineTypeClassName)}>
        {renderCells(oldArgs)}
        {renderCells(newArgs)}
      </tr>
    );
  }
}
